/****************************************************************************
 *
 * Copyright 2016 Samsung Electronics All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific
 * language governing permissions and limitations under the License.
 *
 ****************************************************************************/
/*
 * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 *
 * This file is part of the lwIP TCP/IP stack.
 *
 * Author: Adam Dunkels <adam@sics.se>
 *
 */

/**
 * @file
 * Sequential API Main thread module
 *
 */

#include <debug.h>
#include <stdio.h>
#include <net/lwip/opt.h>

#if !NO_SYS						/* don't build if not configured for use in lwipopts.h */

#include <net/lwip/sys.h>
#include <net/lwip/memp.h>
#include <net/lwip/mem.h>
#include <net/lwip/pbuf.h>
#include <net/lwip/tcpip.h>
#include <net/lwip/init.h>
#include <net/lwip/netif/etharp.h>
#include <net/lwip/netif/ppp_oe.h>

/* global variables */
static tcpip_init_done_fn tcpip_init_done;
static void *tcpip_init_done_arg;
static sys_mbox_t mbox;

#if LWIP_TCPIP_CORE_LOCKING
/** The global semaphore to lock the stack. */
sys_mutex_t lock_tcpip_core;
#endif							/* LWIP_TCPIP_CORE_LOCKING */

/**
 * The main lwIP thread. This thread has exclusive access to lwIP core functions
 * (unless access to them is not locked). Other threads communicate with this
 * thread using message boxes.
 *
 * It also starts all the timers to make sure they are running in the right
 * thread context.
 *
 * @param arg unused argument
 */
static void tcpip_thread(void *arg)
{

	struct tcpip_msg *msg = NULL;
	LWIP_DEBUGF(TCPIP_DEBUG, ("NULL == msg %d \n", NULL == msg));
	LWIP_UNUSED_ARG(arg);
	//LWIP_DEBUGF(TCPIP_DEBUG,("Entry \n"));
	if (tcpip_init_done != NULL) {
		tcpip_init_done(tcpip_init_done_arg);
	}

	LOCK_TCPIP_CORE();
	while (1) {					/* MAIN Loop */
		UNLOCK_TCPIP_CORE();
		LWIP_TCPIP_THREAD_ALIVE();
		/* wait for a message, timeouts are processed while waiting */
		sys_timeouts_mbox_fetch(&mbox, (void **)&msg);

		LOCK_TCPIP_CORE();

		if (msg == NULL) {
			LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: NULL\n"));
			LWIP_ASSERT("tcpip_thread: invalid message", 0);
			continue;
		}

		switch (msg->type) {
#if LWIP_NETCONN
		case TCPIP_MSG_API:
			LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API message %p\n", (void *)msg));
			msg->msg.apimsg->function(&(msg->msg.apimsg->msg));
			break;
#endif							/* LWIP_NETCONN */

#if !LWIP_TCPIP_CORE_LOCKING_INPUT
		case TCPIP_MSG_INPKT:
			LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg));
#if LWIP_ETHERNET
			if (msg->msg.inp.netif->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) {
				ethernet_input(msg->msg.inp.p, msg->msg.inp.netif);
			} else
#endif							/* LWIP_ETHERNET */
			{
				ip_input(msg->msg.inp.p, msg->msg.inp.netif);
			}
			memp_free(MEMP_TCPIP_MSG_INPKT, msg);
			break;
#endif							/* LWIP_TCPIP_CORE_LOCKING_INPUT */

#if LWIP_NETIF_API
		case TCPIP_MSG_NETIFAPI:
			LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: Netif API message %p\n", (void *)msg));
			msg->msg.netifapimsg->function(&(msg->msg.netifapimsg->msg));
			break;
#endif							/* LWIP_NETIF_API */

#if LWIP_TCPIP_TIMEOUT
		case TCPIP_MSG_TIMEOUT:
			LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: TIMEOUT %p\n", (void *)msg));
			sys_timeout(msg->msg.tmo.msecs, msg->msg.tmo.h, msg->msg.tmo.arg);
			memp_free(MEMP_TCPIP_MSG_API, msg);
			break;
		case TCPIP_MSG_UNTIMEOUT:
			LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: UNTIMEOUT %p\n", (void *)msg));
			sys_untimeout(msg->msg.tmo.h, msg->msg.tmo.arg);
			memp_free(MEMP_TCPIP_MSG_API, msg);
			break;
#endif							/* LWIP_TCPIP_TIMEOUT */

		case TCPIP_MSG_CALLBACK:
			LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\n", (void *)msg));
			msg->msg.cb.function(msg->msg.cb.ctx);
			memp_free(MEMP_TCPIP_MSG_API, msg);
			break;

		case TCPIP_MSG_CALLBACK_STATIC:
			LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK_STATIC %p\n", (void *)msg));
			msg->msg.cb.function(msg->msg.cb.ctx);
			break;

		default:
			LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: %d\n", msg->type));
			LWIP_ASSERT("tcpip_thread: invalid message", 0);
			break;
		}
	}
}

/**
 * Pass a received packet to tcpip_thread for input processing
 *
 * @param p the received packet, p->payload pointing to the Ethernet header or
 *          to an IP header (if inp doesn't have NETIF_FLAG_ETHARP or
 *          NETIF_FLAG_ETHERNET flags)
 * @param inp the network interface on which the packet was received
 */
err_t tcpip_input(struct pbuf *p, struct netif *inp)
{
	//LWIP_DEBUGF(TCPIP_DEBUG, ("Entry tcpip_input"));
#if LWIP_TCPIP_CORE_LOCKING_INPUT
	err_t ret;
	LWIP_DEBUGF(TCPIP_DEBUG, ("LWIP_TCPIP_CORE_LOCKING_INPUT: PACKET %p/%p\n", (void *)p, (void *)inp));
	LOCK_TCPIP_CORE();
#if LWIP_ETHERNET
	if (inp->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) {
		ret = ethernet_input(p, inp);
	} else
#endif							/* LWIP_ETHERNET */
	{
		ret = ip_input(p, inp);
	}
	UNLOCK_TCPIP_CORE();
	return ret;
#else							/* LWIP_TCPIP_CORE_LOCKING_INPUT */
	LWIP_DEBUGF(TCPIP_DEBUG, ("MBOX Input Processing, packet will be posted to mbox: PACKET %p/%p\n", (void *)p, (void *)inp));
	struct tcpip_msg *msg;
	//LWIP_DEBUGF(TCPIP_DEBUG, ("Validating mbox"));
	if (!sys_mbox_valid(&mbox)) {
		return ERR_VAL;
	}
	//LWIP_DEBUGF(TCPIP_DEBUG, ("Succesfull Validation mbox"));
	msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_INPKT);
	if (msg == NULL) {
		return ERR_MEM;
	}

	msg->type = TCPIP_MSG_INPKT;
	msg->msg.inp.p = p;
	msg->msg.inp.netif = inp;
	//LWIP_DEBUGF(TCPIP_DEBUG, ("posting msg to mbox"));
	if (sys_mbox_trypost(&mbox, msg) != ERR_OK) {
		memp_free(MEMP_TCPIP_MSG_INPKT, msg);
		return ERR_MEM;
	}
	//LWIP_DEBUGF(TCPIP_DEBUG, ("msg successfully posted to mbox, Exiting"));
	return ERR_OK;
#endif							/* LWIP_TCPIP_CORE_LOCKING_INPUT */
}

/**
 * Call a specific function in the thread context of
 * tcpip_thread for easy access synchronization.
 * A function called in that way may access lwIP core code
 * without fearing concurrent access.
 *
 * @param f the function to call
 * @param ctx parameter passed to f
 * @param block 1 to block until the request is posted, 0 to non-blocking mode
 * @return ERR_OK if the function was called, another err_t if not
 */
err_t tcpip_callback_with_block(tcpip_callback_fn function, void *ctx, u8_t block)
{
	struct tcpip_msg *msg;

	if (sys_mbox_valid(&mbox)) {
		msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
		if (msg == NULL) {
			return ERR_MEM;
		}

		msg->type = TCPIP_MSG_CALLBACK;
		msg->msg.cb.function = function;
		msg->msg.cb.ctx = ctx;
		if (block) {
			sys_mbox_post(&mbox, msg);
		} else {
			if (sys_mbox_trypost(&mbox, msg) != ERR_OK) {
				memp_free(MEMP_TCPIP_MSG_API, msg);
				return ERR_MEM;
			}
		}
		return ERR_OK;
	}
	return ERR_VAL;
}

#if LWIP_TCPIP_TIMEOUT
/**
 * call sys_timeout in tcpip_thread
 *
 * @param msec time in milliseconds for timeout
 * @param h function to be called on timeout
 * @param arg argument to pass to timeout function h
 * @return ERR_MEM on memory error, ERR_OK otherwise
 */
err_t tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg)
{
	struct tcpip_msg *msg;

	if (sys_mbox_valid(&mbox)) {
		msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
		if (msg == NULL) {
			return ERR_MEM;
		}

		msg->type = TCPIP_MSG_TIMEOUT;
		msg->msg.tmo.msecs = msecs;
		msg->msg.tmo.h = h;
		msg->msg.tmo.arg = arg;
		sys_mbox_post(&mbox, msg);
		return ERR_OK;
	}
	return ERR_VAL;
}

/**
 * call sys_untimeout in tcpip_thread
 *
 * @param msec time in milliseconds for timeout
 * @param h function to be called on timeout
 * @param arg argument to pass to timeout function h
 * @return ERR_MEM on memory error, ERR_OK otherwise
 */
err_t tcpip_untimeout(sys_timeout_handler h, void *arg)
{
	struct tcpip_msg *msg;

	if (sys_mbox_valid(&mbox)) {
		msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
		if (msg == NULL) {
			return ERR_MEM;
		}

		msg->type = TCPIP_MSG_UNTIMEOUT;
		msg->msg.tmo.h = h;
		msg->msg.tmo.arg = arg;
		sys_mbox_post(&mbox, msg);
		return ERR_OK;
	}
	return ERR_VAL;
}
#endif							/* LWIP_TCPIP_TIMEOUT */

#if LWIP_NETCONN
/**
 * Call the lower part of a netconn_* function
 * This function is then running in the thread context
 * of tcpip_thread and has exclusive access to lwIP core code.
 *
 * @param apimsg a struct containing the function to call and its parameters
 * @return ERR_OK if the function was called, another err_t if not
 */
err_t tcpip_apimsg(struct api_msg *apimsg)
{
	//LWIP_DEBUGF(TCPIP_DEBUG, ("Entry"));
	struct tcpip_msg msg;
#ifdef LWIP_DEBUG
	/* catch functions that don't set err */
	apimsg->msg.err = ERR_VAL;
#endif

	if (sys_mbox_valid(&mbox)) {
		msg.type = TCPIP_MSG_API;
		msg.msg.apimsg = apimsg;
		sys_mbox_post(&mbox, &msg);
		sys_arch_sem_wait(&apimsg->msg.conn->op_completed, 0);
		LWIP_DEBUGF(TCPIP_DEBUG, ("Exit Success"));
		return apimsg->msg.err;
	}
	//LWIP_DEBUGF(TCPIP_DEBUG, ("Exit Fail"));
	return ERR_VAL;
}

#if LWIP_TCPIP_CORE_LOCKING
/**
 * Call the lower part of a netconn_* function
 * This function has exclusive access to lwIP core code by locking it
 * before the function is called.
 *
 * @param apimsg a struct containing the function to call and its parameters
 * @return ERR_OK (only for compatibility fo tcpip_apimsg())
 */
err_t tcpip_apimsg_lock(struct api_msg *apimsg)
{
#ifdef LWIP_DEBUG
	/* catch functions that don't set err */
	apimsg->msg.err = ERR_VAL;
#endif

	LOCK_TCPIP_CORE();
	apimsg->function(&(apimsg->msg));
	UNLOCK_TCPIP_CORE();
	return apimsg->msg.err;

}
#endif							/* LWIP_TCPIP_CORE_LOCKING */
#endif							/* LWIP_NETCONN */

#if LWIP_NETIF_API
#if !LWIP_TCPIP_CORE_LOCKING
/**
 * Much like tcpip_apimsg, but calls the lower part of a netifapi_*
 * function.
 *
 * @param netifapimsg a struct containing the function to call and its parameters
 * @return error code given back by the function that was called
 */
err_t tcpip_netifapi(struct netifapi_msg *netifapimsg)
{
	struct tcpip_msg msg;

	if (sys_mbox_valid(&mbox)) {
		err_t err = sys_sem_new(&netifapimsg->msg.sem, 0);
		if (err != ERR_OK) {
			netifapimsg->msg.err = err;
			return err;
		}

		msg.type = TCPIP_MSG_NETIFAPI;
		msg.msg.netifapimsg = netifapimsg;
		sys_mbox_post(&mbox, &msg);
		sys_sem_wait(&netifapimsg->msg.sem);
		sys_sem_free(&netifapimsg->msg.sem);
		return netifapimsg->msg.err;
	}
	return ERR_VAL;
}
#else							/* !LWIP_TCPIP_CORE_LOCKING */
/**
 * Call the lower part of a netifapi_* function
 * This function has exclusive access to lwIP core code by locking it
 * before the function is called.
 *
 * @param netifapimsg a struct containing the function to call and its parameters
 * @return ERR_OK (only for compatibility fo tcpip_netifapi())
 */
err_t tcpip_netifapi_lock(struct netifapi_msg *netifapimsg)
{
	LOCK_TCPIP_CORE();
	netifapimsg->function(&(netifapimsg->msg));
	UNLOCK_TCPIP_CORE();
	return netifapimsg->msg.err;
}
#endif							/* !LWIP_TCPIP_CORE_LOCKING */
#endif							/* LWIP_NETIF_API */

/**
 * Allocate a structure for a static callback message and initialize it.
 * This is intended to be used to send "static" messages from interrupt context.
 *
 * @param function the function to call
 * @param ctx parameter passed to function
 * @return a struct pointer to pass to tcpip_trycallback().
 */
struct tcpip_callback_msg *tcpip_callbackmsg_new(tcpip_callback_fn function, void *ctx)
{
	struct tcpip_msg *msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
	if (msg == NULL) {
		return NULL;
	}
	msg->type = TCPIP_MSG_CALLBACK_STATIC;
	msg->msg.cb.function = function;
	msg->msg.cb.ctx = ctx;
	return (struct tcpip_callback_msg *)msg;
}

/**
 * Free a callback message allocated by tcpip_callbackmsg_new().
 *
 * @param msg the message to free
 */
void tcpip_callbackmsg_delete(struct tcpip_callback_msg *msg)
{
	memp_free(MEMP_TCPIP_MSG_API, msg);
}

/**
 * Try to post a callback-message to the tcpip_thread mbox
 * This is intended to be used to send "static" messages from interrupt context.
 *
 * @param msg pointer to the message to post
 * @return sys_mbox_trypost() return code
 */
err_t tcpip_trycallback(struct tcpip_callback_msg *msg)
{
	if (!sys_mbox_valid(&mbox)) {
		return ERR_VAL;
	}
	return sys_mbox_trypost(&mbox, msg);
}

/**
 * Initialize this module:
 * - initialize all sub modules
 * - start the tcpip_thread
 *
 * @param initfunc a function to call when tcpip_thread is running and finished initializing
 * @param arg argument to pass to initfunc
 */
void tcpip_init(tcpip_init_done_fn initfunc, void *arg)
{
	//LWIP_DEBUGF(TCPIP_DEBUG, ("Entry"));
	tcpip_init_done = initfunc;
	tcpip_init_done_arg = arg;
	//LWIP_DEBUGF(TCPIP_DEBUG, ("creating mbox"));
	if (sys_mbox_new(&mbox, TCPIP_MBOX_SIZE) != ERR_OK) {
		LWIP_ASSERT("failed to create tcpip_thread mbox", 0);
	}
	//LWIP_DEBUGF(TCPIP_DEBUG, ("mbox created"));
#if LWIP_TCPIP_CORE_LOCKING
	if (sys_mutex_new(&lock_tcpip_core) != ERR_OK) {
		LWIP_ASSERT("failed to create lock_tcpip_core", 0);
	}
#endif							/* LWIP_TCPIP_CORE_LOCKING */
	//LWIP_DEBUGF(TCPIP_DEBUG, ("creating new thread for tcpip"));
	sys_kernel_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO);
	//LWIP_DEBUGF(TCPIP_DEBUG, ("Exit"));
}

/**
 * Simple callback function used with tcpip_callback to free a pbuf
 * (pbuf_free has a wrong signature for tcpip_callback)
 *
 * @param p The pbuf (chain) to be dereferenced.
 */
static void pbuf_free_int(void *p)
{
	struct pbuf *q = (struct pbuf *)p;
	pbuf_free(q);
}

/**
 * A simple wrapper function that allows you to free a pbuf from interrupt context.
 *
 * @param p The pbuf (chain) to be dereferenced.
 * @return ERR_OK if callback could be enqueued, an err_t if not
 */
err_t pbuf_free_callback(struct pbuf *p)
{
	return tcpip_callback_with_block(pbuf_free_int, p, 0);
}

/**
 * A simple wrapper function that allows you to free heap memory from
 * interrupt context.
 *
 * @param m the heap memory to free
 * @return ERR_OK if callback could be enqueued, an err_t if not
 */
err_t mem_free_callback(void *m)
{
	return tcpip_callback_with_block(mem_free, m, 0);
}

#endif							/* !NO_SYS */
